// Author : Zhuowen Tu
/*
    This header file defines all the classes corresponding to the components of which
	the graph is formed. A entire graph is composed of many regions, which are formed
	by contour segments. A region will possibly have many holes whose Ids are denoted
	by hole array in region class. Each contour segment remembers it two extreme vertices and may
	be shared at most by two regions. The left region and right region Ids are stored in
	each contour segment object as well. Each vertex is explictly expressed by a vertex
	object. Besides this, a node object is reduntauntly associated with each vertix, where
	the same 2d corrdinates are saved.
*/

#ifndef _MCMC_LAYERREGION_H
#define _MCMC_LAYERREGION_H

#include "mcmcLayerRegionElements.h"

class MCMC_MoveBoundary;

//////////////////////////////////////////////////////////
/* This class is the master of all classes declared in this
   file. The whole graph is represented in this class.
*/

class MCMC_Layer_Regions
{
public:
	// construction & destruction
	MCMC_Layer_Regions();
	~MCMC_Layer_Regions();
	virtual void Free(void);
	virtual void Init(void);

	virtual void Empty(void);
	virtual void DeleteAllGroups(void);

	// operators
	virtual void operator>>( ofstream &strm);
	virtual void operator<<( ifstream &strm);
	MCMC_Layer_Regions& operator=(MCMC_Layer_Regions &a);
	void	SaveToFile(char *file_name);
	bool	ReadFromFile(char *file_name);

	// access
	inline void	AssertRegion(const int id);
	inline void	AssertVertex(const int id);
	inline void	AssertNode(const int id);
	inline void	AssertCSeg(const int id);

	inline MCMC_Contour_Segment*GetCSeg(const int id) const;
	inline MCMC_Node*			GetNode(const int id) const;
	inline MCMC_Vertex*			GetVertex(const int id) const ;
	inline MCMC_AtomicRegion*	GetAtomicRegion(const int id) const;
	inline MCMC_SortedNodes*	GetSortedNodes(const int id);
	inline MCMC_RegionElement*	GetRegionFromIndexId(const int indexid);
	inline MCMC_GRegion*		GetGRegion(const int id);

	inline int	GetFirstVer(void){return management_vertices.GetFirst();};
	inline int	GetNextVer(void){return management_vertices.GetNext();};
	inline int	GetFirstCSeg(void){return management_csegments.GetFirst();};
	inline int	GetNextCSeg(void){return management_csegments.GetNext();};
	inline int	GetFirstAtomicRegion(void){return management_atomic_regions.GetFirst();};
	inline int	GetNextAtomicRegion(void){return management_atomic_regions.GetNext();};
	inline int	GetFirstGRegion(void){return management_gregions.GetFirst();};
	inline int	GetNextGRegion(void){return management_gregions.GetNext();};

	void		GetAllRegions(LinkList<int> &list_indexid);
	void		GetAllTopNodeRegions(LinkList<int> &list_indexid);
	void		AllCSegToMatrix(mcmcVector<int> &mx_cseg);				// copy all the boundary Ids to a matrix
	inline double EnergyTotal(void){return energy_total;};
	inline double EnergyLoglik(void){return energy_loglik;};

	int			GetCSegOfVertices(const int id_ver1, const int id_ver2,
								  const int x_pass=-1, const int y_pass=-1);	// get the boundary on vertex 'id_ver1' and 'id_ver2'
	inline int	NumCSegments(void) const {return management_csegments.Num();};
	inline int	NumVertices(void) const {return management_vertices.Num();};
	inline int	NumAtomicRegions(void)const {return management_atomic_regions.Num();};		// count the number of all the regions
	inline int	NumGRegions(void) const {return management_gregions.Num();};
	inline int	NumNodes(void) const {return management_nodes.Num();};

	inline int	MaxNodeSpace(void) const {return management_nodes.Max();};
	inline int	MaxVertexSpace(void) const {return management_vertices.Max();};
	inline int	MaxCSegSpace(void) const {return management_csegments.Max();};
	inline int	MaxAtomicRegionSpace(void) const {return management_atomic_regions.Max();};
	inline int	MaxGRegionSpace(void) const {return management_gregions.Max();};
	inline int	MaxGRegionIndex(void) const {return MaxAtomicRegionSpace()+MaxGRegionSpace()+SHIFT_GREGION;};

	// get the tempture
	inline double GetT(void){return 0.2;};

	// operations
	void		InitilizeAllMembers(void);
	int			NewVertex(void);													// create a new vertex
	int			NewNode(void);														// create a new node
	int			NewCSeg(void);														// create a new contour segment
	int			NewAtomicRegion(void);												// create a new region
	int			NewGRegion(void);

	void		DeleteAtomicRegion(const int id_region);
	void		DeleteAtomicRegionFromArray(const int id_rgn);						// delete a region
	void		DeleteCSegFromArray(const int id_seg, const bool bDeleteNode=false);// delete a contour segment
	void		DeleteNodeFromArray(const int id_node);								// delete a node
	void		DeleteVertexFromArray(const int id_ver);							// delete a vertex
	void		DeleteGRegion(const int id_gregion);

	int			MergeRegions(int id_rgn1, int id_rgn2);
	mcmcVector<int>	MergeRegions(mcmcVector<int> &input_region_id);
	void		ComputeTotalEnergy(const bool brecompute_energyshape);
	bool		OnImageBound(const double x, const double y);
	bool		OnImageCorner(const double x, const double y);
	// save this segmentation result to a file in '.seg' format for malik's benchmark
	bool		SaveToMalikSegFile(const char *fname, const int height_org, const int width_org, const char *image_id, const bool bcolor);
	void		DeleteNoValidRegion(void);

	int		TotalCodingBits(void);
	int			SplitAtomicRegion(const int id_aregion, Raster<int> &ras_partition_map, mcmcVector<int> *pver_new_id=NULL);

	// group operations
	bool		IsInGRegion(const int id_region, const int id_gregion);
	inline bool	IsAtomicRegion(const int indexid);
	void		PutAllAtomicRegionsIntoGRegions(void);
	int			GroupIntoNewGRegion(const int index_id1, const int index_id2);
	int			GroupGRegions(const int index_id1, const int index_id2);

	// affinity
	void		CreatePixelDomain(void);
	MCMC_CombinedAffinity*	GetAffinityBetwARegions(const int id_aregion1, const int id_aregion2);
	void		CreateAffinityMap(MCMC_FilterResp &resp, LinkList<int> *plist_aregions=NULL);
	void		DeleteAffinity(LinkList<int> &list_aregion_tochange);
	void		UpdateAffinityMap(MCMC_FilterResp &resp);
	void		UpdateAffinityMap(MCMC_FilterResp &resp, LinkList<int> *plist);
	bool		SaveAffinityMap(char *file_name);
	
	void		GetAllAtomicRegionIn(LinkList<int> &list_aregion, LinkList<int> &list_picked);
	LinkList<int>*	FindClosedContours(int &num, LinkList<int> &list_aregions);
	void		CleanTopologyForGRegions(void);
	void		DeleteAllEmptyGRegions(void);
	
	void		CheckGRegionTopology(void);

public:
	int			MergeSegments(const int id_seg1, const int id_seg2, const int id_region);
	int			MergeSegments(const int id_seg1, const int id_seg2);
	void		FindNewHoles(const int id_rgn1,const int id_rgn2,LinkList<int> &list);
	void		SetHoleRelationship(void);
	int			Build(Raster<int> &ras_patition, int num_of_region=0, MCMC_AtomicRegion *pregion_sour=NULL, mcmcVector<int> *pnew_region_id=NULL,const bool bDelNoValid=true,
					  const bool bResetHole=true);
	void		DrawRegions(Raster<int> &ras_boundaries);

	void		LabelAtomicRegions(Raster<int> &ras_label_map);
	void		LabelMapTopRegions(Raster<int> &ras_label_top);

	MCMC_Edge_Image	*OuterBundaries(Raster<int> &ras_label_map, int num_to_region=0);
	double		GetShapeLogPriorMerge(mcmcMatrix<int> id_regions);
	bool		MergeSmallAtomicRegion(const int min_size=MINIMAL_SIZE);
	void		Recover(Raster<int> &ras_label_map);
	void		Smooth(void);
	void		SortRegionNodes(LinkList<int> *plist_region=NULL);
	double		NodeDistance(const int id_node1, const int id_node2);
	int			KeepCurrent(void);
	void		RestoreCurrent(const int temp);
	void		Synthesize(Raster_Color<uchar> &raster_color, mcmcMatrix<int> *pmx_label=NULL);
	void		Reconstruct(Raster_Color<uchar> &raster_color);
	void		Reconstruct(Raster<double> &raster_region, Raster<int> &ras_label, LinkList<int> &list_regions);
	void		ReGenerateNbRelationships(void);
	void		CreateTopologyRecords(void);

	int			MoveNodes(const int id_cseg, const int id_node1, const int id_node2, const uchar left_or_right);

private:
	void		InitAllNodes(MCMC_Node *pAllNodes, const int max_space);
	void		InitAllVertices(MCMC_Vertex *pAllVertices, const int max_space);
	void		InitAllCSeg(MCMC_Contour_Segment *pAllCSeg, const int max_space);
	void		InitAllAtomicRegions(MCMC_AtomicRegion *pAllRegions, const int max_space);
	void		InitAllGeneralizedRegions(MCMC_GRegion *pAllRegions, const int max_space);
	void		GrowNodesBy(void);
	void		GrowVerticesBy(void);
	void		GrowCSegBy(void);
	void		GrowRegionsBy(void);

	bool		IsBoundaryPosition(Raster<int> &image, const int x, const int y, int t[]);
	int			BoundaryDirection(Raster<int> &image, const int x, const int y, const int last_k, const int curr_k);
	void		GetNeighbors(Raster<int> &image, const MCMC_Point &pt1,const MCMC_Point &pt2, int label[]);
	void		SetUpBoundaryCond1(MCMC_AtomicRegion &region, mcmcMatrix<uchar> &matrix_temp1,mcmcMatrix<int> &matrix_ver, mcmcMatrix<int> &matrix_node);
	int			BuildCreateVertexAndRegions(Raster<int> &image, MCMC_Edge_Image	*pedge_image,
										  mcmcMatrix<int> &matrix_ver, mcmcMatrix<uchar> &matrix_temp1,
										  mcmcMatrix<int> &matrix_node, mcmcMatrix<int> &matrix_region_id,
										  mcmcMatrix<int> &matrix_bundary, mcmcVector<int> *pnew_region_id);
	void		SetUpBoundaryCond2(MCMC_AtomicRegion &region, mcmcMatrix<uchar> &matrix_temp1,mcmcMatrix<int>	&matrix_node, mcmcMatrix<int>& matrix_bundary);
	void		BuildNewHoles(MCMC_Edge_Image	*pedge_image,mcmcMatrix<int> &matrix_ver, mcmcMatrix<uchar> &matrix_temp1,
						      mcmcMatrix<int> &matrix_node, mcmcMatrix<int> &matrix_bundary);
	void		BreakUpOldSegment(MCMC_AtomicRegion &region, mcmcMatrix<int> &matrix_ver,
									mcmcMatrix<uchar> &matrix_temp1,mcmcMatrix<int> &matrix_node);
	void		OnBoundNode(MCMC_AtomicRegion &region,mcmcMatrix<uchar> &matrix_temp1);
	void		BuildPassExisting(int id_cseg,Raster<int> &image,mcmcMatrix<int> &matrix_region_id, MCMC_AtomicRegion *pregion_sour);
	void		SetregionlistDuetoMerge(LinkList<int> &list_region, const int id_region1, const int id_region2);
	void		DanglingRegions(void);


public:
	// permanent
	MCMC_MoveBoundary	*pmb_procedure;

	// variables
	int			height,width;
	double		scale_factor;

	// contingent
	// to keep track of the topology change
	Raster<int>			mx_label_id_aregions;		// each pixel rememer the id of the atomic region to which it belongs
	mcmcMatrix<int>		mx_label_id_nodes;			// size: Height+1 X Width+1
	mcmcMatrix<int>		mx_label_id_vertices;		// size: Height+1 X Width+1
	MCMC_CSegOnGrid		cseg_on_grid;

public:
	MCMC_MemoryNodeManagement	management_csegments;

protected:
	// variables

	// space for saving regions, vertices, segments and nodes
	MCMC_Node			*p_nodes;
	MCMC_Contour_Segment*p_csegments;
	MCMC_Vertex			*p_vertices;
	MCMC_AtomicRegion	*p_atomic_regions;
	MCMC_GRegion		*p_gregions;

	double		energy_total;
	double		energy_loglik;

	MCMC_MemoryNodeManagement	management_nodes;
	MCMC_MemoryNodeManagement	management_vertices;
	MCMC_MemoryNodeManagement	management_atomic_regions;
	MCMC_MemoryNodeManagement	management_gregions;

private:
	Raster<int>			ras_temp_for_split;
};

inline MCMC_AtomicRegion* MCMC_Layer_Regions::GetAtomicRegion(const int index) const
{
	if (index<0 || index>=management_atomic_regions.Max())
		return NULL;
	else
		return &(p_atomic_regions[index]);
}

inline MCMC_GRegion* MCMC_Layer_Regions::GetGRegion(const int id)
{
	if (id<0 || id>=management_gregions.Max())
		return NULL;
	else
		return &(p_gregions[id]);
}

inline MCMC_RegionElement* MCMC_Layer_Regions::GetRegionFromIndexId(const int indexid)
{
	if (indexid < SHIFT_GREGION)
	{
		if (indexid<0 || indexid>=management_atomic_regions.Max())
			return NULL;
		else
			return &(p_atomic_regions[indexid]);
	}
	else
	{
		int	id_temp=indexid-SHIFT_GREGION;

		if (id_temp<0 || id_temp>=management_gregions.Max())
			return NULL;
		else
			return &(p_gregions[id_temp]);
	}
}

inline bool	MCMC_Layer_Regions::IsAtomicRegion(const int indexid)
{
	if (indexid >= SHIFT_GREGION)
		return false;
	else
		return true;
}



inline MCMC_SortedNodes* MCMC_Layer_Regions::GetSortedNodes(const int id)
{
	MCMC_AtomicRegion* pregion;
	pregion = GetAtomicRegion(id);
	if (pregion!=NULL)
		return ((MCMC_SortedNodes*)pregion);
	else
		return NULL;
}


inline MCMC_Vertex*	MCMC_Layer_Regions::GetVertex(const int index) const
{
	if (index<0 || index>=management_vertices.Max())
		return NULL;
	else
		return &(p_vertices[index]);
}

inline MCMC_Contour_Segment* MCMC_Layer_Regions::GetCSeg(const int index) const
{
	if (index<0 || index>=management_csegments.Max())
		return NULL;
	else
		return &(p_csegments[index]);
}

inline MCMC_Node* MCMC_Layer_Regions::GetNode(const int index) const
{
	if (index<0 || index>=management_nodes.Max())
		return NULL;
	else
		return &(p_nodes[index]);
}


#endif
